#!/usr/bin/env python

import pygame
from pygame.locals import *

import helper

from helper import get_prop
from image import rgb_to_hex, hex_to_rgb

CHARSET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()-=_+[]{}\\|"\',./<>? '

DEFAULT_FONT = 'default'
SYSTEM_FONT = 'system'

font_cache = { }

class TextError(Exception):
  def __init__(self, value):
    self.value = value
  
  def __str__(self):
    return repr(self.value)

class TextCache:
  def __init__(self, font_fn, **properties):
    size = get_prop(properties, 'size', 12)
    colors = get_prop(properties, 'colors', ['000000'])
    charset = get_prop(properties, 'charset', CHARSET)
    
    self.antialias = get_prop(properties, 'antialias', False)
    self.xspacing = get_prop(properties, 'xspacing',  int(size * 0.2))
    self.yspacing = get_prop(properties, 'yspacing', int(size * 0.1))
    
    self.color_list = map(lambda c: hex_to_rgb(c), colors)
    self.font = pygame.font.Font(font_fn, size)
    self.cache = { }
    for color in self.color_list:
      self.cache[color] = { }
      for char in charset:
        char_size = self.font.size(char)
        char_surf = self.font.render(char, self.antialias, color)
        self.cache[color][char] = char_surf
  
  def render(self, text, color, **properties):
    xalign = get_prop(properties, 'xalign', 'left')
    yalign = get_prop(properties, 'yalign', 'bottom')
    max_width = get_prop(properties, 'max_width', -1)
    
    if color not in self.color_list:
      raise TextError('Color %s not found in the cache; cannot render.' % (color))
    
    char_cache = self.cache[color]
    
    if max_width == -1:
      max_width = reduce(lambda x, c: x + char_cache[c].get_rect().w, text, 0) + self.xspacing * (len(text) - 1)
    
    #TODO handle word wrapping
    max_height = reduce(lambda x, c: x > char_cache[c].get_rect().h and x or char_cache[c].get_rect().h, text, 0)
    
    text_surf = pygame.Surface((max_width, max_height), SRCALPHA, 32)
    
    curr_pos = 0
    i = 0
    while curr_pos < max_width:
      if i == len(text):
        break
      char = text[i]
      char_surf = char_cache[char]
      rect = char_surf.get_rect()
      
      x_pos = curr_pos
      y_pos = max_height - rect.h
      
      text_surf.blit(char_surf, (x_pos, y_pos))
      
      i += 1
      curr_pos = curr_pos + rect.w + self.xspacing
      #curr_pos = curr_pos + rect.w
    
    return text_surf
  


def load_font(font_fn, name, **properties):
  if name in font_cache:
    raise TextError('Font with name \'%s\' already in cache.' % (name))
  
  size = get_prop(properties, 'size', 18)
  set_as_default = get_prop(properties, 'set_as_default', False)
  
  if set_as_default: name = DEFAULT_FONT
  
  font = pygame.font.Font(font_fn, size)
  
  char_sizes = map(lambda x: font.size(x), CHARSET)
  line_height = reduce(lambda x, y: x > y[1] and x or y[1], char_sizes, 0)
  
  
  font_cache[name] = (font, line_height)

def get_system_font():
  if SYSTEM_FONT not in font_cache:
    default = DEFAULT_FONT not in font_cache
    load_font(None, SYSTEM_FONT, set_as_default=default)
  f = get_font(SYSTEM_FONT)
  return f
    

def get_default_font():
  return get_font(DEFAULT_FONT)

def get_font(font_name):
  return font_cache[font_name][0]

def render(text, dest_surf=None, font_name=None, **properties):
  color = get_prop(properties, 'color', (255, 255, 255))
  antialias = get_prop(properties, 'antialias', True)
  start_pos = get_prop(properties, 'start_pos', (0, 0))
  
  yspacing = 2
  
  if font_name is None: font_name = DEFAULT_FONT
  
  font, line_height = font_cache[font_name]
  
  def line_width(line):
    return reduce(lambda x, y: x + font.size(y)[0], line, 0)
  
  lines = text.split('\n')
  line_widths = map(lambda x: line_width(x), lines)
  max_line_width = reduce(lambda x, y: x > y and x or y, line_widths)
  
  if dest_surf is None:
    dest_surf = pygame.Surface((max_line_width, 
                                line_height * len(lines) + yspacing * (len(lines) - 1)), 
                                SRCALPHA, 
                                32)
  
  #text_surf = pygame.Surface((max_line_width, line_height * len(lines) + yspacing * (len(lines) - 1)), SRCALPHA, 32)
  curr_x_pos, curr_y_pos = start_pos
  for line in lines:
    line_surf = font.render(line, antialias, color)
    dest_surf.blit(line_surf, (curr_x_pos, curr_y_pos))
    curr_y_pos = curr_y_pos + line_height + yspacing
  return dest_surf
  

import sys

if __name__ == "__main__":
  pygame.init()
  
  screen = pygame.display.set_mode((300, 200))
  
  '''
  tc = TextCache('vlgothic.ttf', size=18, colors=['ffffff'], xspacing=0, antialias=True)
  tt = tc.render('fucker you', (255, 255, 255))
  
  screen.blit(tt, (100, 100))
  
  f = pygame.font.Font('vlgothic.ttf', 18)
  tt2 = f.render('fucker you', True, (255, 255, 255))
  
  screen.blit(tt2, (100, 150))
  '''
  
  load_font('vlgothic.ttf', 'vlgothic', size=18, set_as_default=True)
  render('I have a dream.\nJust do it.', screen, start_pos=(100, 100))
  
  while True:
    for event in pygame.event.get():
      if event.type == pygame.QUIT:
        pygame.quit()
        sys.exit()
    
    pygame.display.update()
    pygame.time.wait(100)
